﻿using System.Collections.Generic;
using System.Linq;

namespace BowlingKata
{
    /// <summary>
    /// This class represents a frame in a bowling game.
    /// </summary>
    public class Frame
    {
        private static readonly Frame NullFrame = new Frame();

        /// <summary>
        /// Create a new Frame.
        /// </summary>
        public Frame()
        {
            Shots = new List<int>();
            NextFrame = NullFrame;
        }

        protected List<int> Shots { get; set; }

        /// <summary>
        /// Gets or sets the next frame of this frame.
        /// </summary>
        public Frame NextFrame { get; set; }

        /// <summary>
        /// Gets the total score of the frame with the bonus.
        /// </summary>
        public int TotalScore
        {
            get { return IsTotalScoreValid ? Score + Bonus : 0; }
        }

        /// <summary>
        /// The score of the frame without considering the bonus.
        /// </summary>
        public int Score
        {
            get { return Shots.Sum(); }
        }

        /// <summary>
        /// Gets a value indicating whether the total score value can be read and is valid.
        /// </summary>
        public virtual bool IsTotalScoreValid
        {
            get
            {
                if (!IsSpareOrStrike && IsTwoShots)
                {
                    return true;
                }

                if (IsStrike)
                {
                    if (NextFrame.IsTwoShots)
                    {
                        return true;
                    }

                    if (NextFrame.IsStrike && NextFrame.NextFrame.IsOneShotAvailable)
                    {
                        return true;
                    }
                }

                return IsSpare && NextFrame.IsOneShotAvailable;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the frame is complete.
        /// </summary>
        public bool IsComplete
        {
            get { return IsStrike || IsTwoShots; }
        }

        /// <summary>
        /// Gets the bonus for the frame.
        /// </summary>
        public virtual int Bonus
        {
            get
            {
                if (IsStrike)
                {
                    if (NextFrame.IsTwoShots)
                    {
                        return NextFrame.Score;
                    }

                    if (NextFrame.IsStrike && NextFrame.NextFrame.Shots.Count > 0)
                    {
                        return 10 + NextFrame.NextFrame.Shots[0];
                    }
                }

                if (IsSpare && NextFrame.IsOneShotAvailable)
                {
                    return NextFrame.Shots[0];
                }

                return 0;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the frame is a spare.
        /// </summary>
        public bool IsSpare
        {
            get { return IsTwoShots && Shots[0] + Shots[1] == 10; }
        }

        /// <summary>
        /// Gets a value indicating whether the frame is a strike.
        /// </summary>
        public bool IsStrike
        {
            get { return Shots.Count == 1 && Shots[0] == 10; }
        }

        /// <summary>
        /// Gets a value indicatid whether the frame is a spare or a strike.
        /// </summary>
        public bool IsSpareOrStrike
        {
            get { return IsSpare || IsStrike; }
        }

        /// <summary>
        /// Gets a value indicating whether the frame contains a single shot.
        /// </summary>
        internal bool IsOneShotAvailable
        {
            get { return Shots.Count >= 1; }
        }

        /// <summary>
        /// Gets a value indicating whether the frame contains two shots.
        /// </summary>
        internal bool IsTwoShots
        {
            get { return Shots.Count == 2; }
        }

        /// <summary>
        /// Returns a representation of the Frame as a string.
        /// </summary>
        public override string ToString()
        {
            if (Shots.Count == 0)
            {
                return "--";
            }

            if (!IsSpareOrStrike)
            {
                if (Shots.Count == 1)
                {
                    return string.Format("{0}-", Shots[0]);
                }

                return string.Format("{0}{1}", Shots[0], Shots[1]);
            }

            if (IsStrike)
            {
                return "X";
            }

            return string.Format("{0}/", Shots[0]);
        }

        /// <summary>
        /// Shot a number of pins in the frame.
        /// </summary>
        internal void Shot(int pins)
        {
            Shots.Add(pins);
        }
    }
}
